#include "MessagePackWriter.h"
#include "../../jtt809_logger.h"
#include "../util/utils.h"
#include <boost/crc.hpp>

Logger logger = JTT809Logger::logger("MessagePackWriter");
using namespace protocol;

bool MessagePackWriter::tryEncode(const uint8_t& to_encode, uint16_t &byte) const {
    switch (to_encode) {
        case 0x5b: {
            byte = 0x5a01;
            break;
        }
        case 0x5a: {
            byte = 0x5a02;
            break;
        }
        case 0x5d: {
            byte = 0x5e01;
            break;
        }
        case 0x5e: {
            byte = 0x5e02;
            break;
        }
        default: {
            return false;
        }
    }
    return true;
}

void MessagePackWriter::writeByte(const uint8_t byte, const bool encode) {
    if (uint16_t to_write; encode && tryEncode(byte, to_write)) {
        const auto net = boost::endian::native_to_big(to_write);
        memcpy(buffer+writePosition++, &net, sizeof(to_write));
        writePosition++;
    }
    else
        memcpy(buffer+writePosition++,&byte, sizeof(byte));
    memcpy(raw+rawPosition++,&byte, sizeof(byte));
}

void MessagePackWriter::writeBytes(const uint8_t *bytes, const size_t len) {
    for (int i = 0; i < len; ++i) {
        const auto byte = bytes[i];
        writeByte(byte);
    }
}

void MessagePackWriter::writeTime(const time_t time) {
    auto net = boost::endian::native_to_big(time);
    const auto to_encode = reinterpret_cast<unsigned char*>(&net);
    for (int i = 0; i < sizeof(time_t); ++i) {
        writeByte(to_encode[i]);
    }
}

void MessagePackWriter::writeCrcCodeAndEndFlag(const uint8_t end) {
    boost::crc_ccitt_false_t computer;
    computer.process_bytes(raw+1, rawPosition-1);
    // logger->info(toHex(raw+1, rawPosition-1));
    const auto crc = computer.checksum();
    // logger->info("CRC code is calculated as 0x{}", toHex(&crc, 2));
    writeUint(static_cast<uint16_t>(crc));
    writeByte(end, false);
}

std::shared_ptr<char[]> MessagePackWriter::flushAndGet() const {
    const auto to_return = new char[writePosition];
    memcpy(to_return, buffer, writePosition);
    std::shared_ptr<char[]> smart_return(to_return);
    return smart_return;
}

size_t MessagePackWriter::size() const {
    return writePosition;
}

void MessagePackWriter::combine(const MessagePackWriter &writer) {
    if (const auto combine_size = this->writePosition + writer.writePosition; combine_size >= bufferSize) {
        logger->info("The combine_size:{}, which is larger than bufferSize:{}");
        // reallocate memory
        auto *temp = new byte[combine_size+1];
        memcpy(temp, buffer, writePosition);
        memcpy(temp+writePosition, writer.buffer, writer.writePosition);
        delete[] buffer;
        buffer = temp;
    }
    else {
        memcpy(buffer+writePosition, writer.buffer, writer.writePosition);
        memcpy(raw+rawPosition, writer.raw, writer.rawPosition);
    }

    writePosition += writer.writePosition;
    rawPosition += writer.rawPosition;
}

MessagePackWriter::~MessagePackWriter() {
    delete[] buffer;
    delete[] raw;
}
